{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Introduction\n",
    "\n",
    "`DiscreteEvents.jl` provides a **clock** with a virtual simulation time and the ability to schedule Julia functions and expressions as events on the clock's timeline or run them as processes synchronizing with the clock. The clock can invoke registered functions or expressions continuously with a given sample rate.\n",
    "\n",
    "## A first example\n",
    "\n",
    "A server takes something from its input and puts it out modified after some time. We implement the server's activity in a function, create input and output channels and some \"foo\" and \"bar\" processes interacting on them:  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " 0.00: foo 1 took token 1\n",
      " 0.77: bar 2 took token 2\n",
      " 1.71: foo 3 took token 4\n",
      " 2.38: bar 4 took token 7\n",
      " 2.78: foo 5 took token 28\n",
      " 3.09: bar 6 took token 33\n",
      " 3.75: foo 7 took token 198\n",
      " 4.34: bar 8 took token 205\n",
      " 4.39: foo 1 took token 1640\n",
      " 4.66: bar 2 took token 1641\n",
      " 4.77: foo 3 took token 3282\n",
      " 4.93: bar 4 took token 3285\n",
      " 5.41: foo 5 took token 13140\n",
      " 6.27: bar 6 took token 13145\n",
      " 6.89: foo 7 took token 78870\n",
      " 7.18: bar 8 took token 78877\n",
      " 7.64: foo 1 took token 631016\n",
      " 7.91: bar 2 took token 631017\n",
      " 8.36: foo 3 took token 1262034\n",
      " 8.94: bar 4 took token 1262037\n",
      " 9.20: foo 5 took token 5048148\n",
      " 9.91: bar 6 took token 5048153\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"run! finished with 43 clock events, 0 sample steps, simulation time: 10.0\""
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "using DiscreteEvents, Printf, Random\n",
    "reset!(𝐶)                                ### reset the central clock\n",
    "\n",
    "function serve(input::Channel, output::Channel, name, id, op)\n",
    "    token = take!(input)                 ### take something from the input\n",
    "    now!(SF(println, @sprintf(\"%5.2f: %s %d took token %d\", tau(), name, id, token)))\n",
    "    delay!(rand())                       ### after a delay\n",
    "    put!(output, op(token, id))          ### put it out with some op applied\n",
    "end\n",
    "\n",
    "ch1 = Channel(32)                        ### create two channels\n",
    "ch2 = Channel(32)\n",
    "\n",
    "for i in 1:2:8      ### create, register and start 8 SimProcesses (alias SP)\n",
    "    process!(SP(i, serve, ch1, ch2, \"foo\", i, +))\n",
    "    process!(SP(i+1, serve, ch2, ch1, \"bar\", i+1, *))\n",
    "end\n",
    "\n",
    "Random.seed!(123)\n",
    "put!(ch1, 1)                             ### put first token into channel 1\n",
    "run!(𝐶, 10)                              ### run for 10 time units"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Four building blocks\n",
    "\n",
    "`DiscreteEvents.jl` provides 4 major building blocks for modeling and simulation of discrete event systems:\n",
    "\n",
    "1. the **clock** gives a virtual simulation time,\n",
    "2. **events** are expressions or functions scheduled for execution at given times or conditions,\n",
    "3. **processes** run asynchronously and can delay for a time or wait for conditions,\n",
    "4. **continuous sampling** allows continuous operations on the time line.\n",
    "\n",
    "## The clock\n",
    "\n",
    "The clock is central to any model and simulation, since it establishes the timeline. It does not only provide the time, but contains also the time unit, all scheduled events, conditional events, processes, sampling expressions or functions and the sample rate Δt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Clock: state=DiscreteEvents.Undefined(), time=0.0, unit=, events: 0, cevents: 0, processes: 0, sampling: 0, sample rate Δt=0.0"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c = Clock()                              ### create a new clock"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.0: tick!\n",
      "1.0: tick!\n",
      "2.0: tick!\n",
      "3.0: tick!\n",
      "4.0: tick!\n",
      "5.0: tick!\n",
      "6.0: tick!\n",
      "7.0: tick!\n",
      "8.0: tick!\n",
      "9.0: tick!\n",
      "10.0: tick!\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"run! finished with 11 clock events, 0 sample steps, simulation time: 10.0\""
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tick() = println(tau(c), \": tick!\")      ### define a function printing the clock's time\n",
    "event!(c, SF(tick), every, 1)            ### schedule a repeat event on the clock\n",
    "run!(c, 10)                              ### run the clock for 10 time units"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10.0"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tau(c)                                   ### tau gives the clock's time"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You normally use the *central clock* `𝐶` (\\it𝐶+tab), alias `Clk`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Clock: state=DiscreteEvents.Idle(), time=0.0, unit=, events: 0, cevents: 0, processes: 0, sampling: 1, sample rate Δt=1.0"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "reset!(𝐶)                                ### reset the central time\n",
    "tick() = println(tau(), \": tick!\")       ### the tick function now uses central time tau()\n",
    "sample_time!(1)                          ### set the sampling rate on the central clock to 1\n",
    "sample!( SF(tick) );                     ### set tick as a sampling function\n",
    "𝐶                                        ### 𝐶 now has one sampling entry and the sample rate set"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.0: tick!\n",
      "2.0: tick!\n",
      "3.0: tick!\n",
      "4.0: tick!\n",
      "5.0: tick!\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"run! finished with 0 clock events, 5 sample steps, simulation time: 5.0\""
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "run!(𝐶, 5)                               ### run 𝐶 for 5 time units"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6.0: tick!\n",
      "7.0: tick!\n",
      "8.0: tick!\n",
      "9.0: tick!\n",
      "10.0: tick!\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"run! finished with 0 clock events, 5 sample steps, simulation time: 10.0\""
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "run!(𝐶, 5)                               ### run it again"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If Δt = 0, the clock doesn't tick with a fixed interval, but jumps from event to event.\n",
    "\n",
    "## Events\n",
    "\n",
    "Julia *functions* or *expressions* are scheduled as events on the clock's time line. In order to not be invoked immediately,\n",
    "\n",
    "- expressions must be [quoted](https://docs.julialang.org/en/v1/manual/metaprogramming/#Quoting-1) with `:()` and\n",
    "- functions must be enclosed inside a `SimFunction`, alias `SF`\n",
    "\n",
    "Quoted expressions and SimFunctions can be given to events mixed in a tuple or array. \n",
    "\n",
    "### Timed events\n",
    "\n",
    "Timed events with [`event!`](@ref event!(::Clock, ::Union{SimExpr, Array, Tuple}, ::Number)) schedule events to execute at a given time:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.0: I'm a SimFunction\n",
      "2.0: I'm a quoted expression\n",
      "5.0: I'm a SimFunction\n",
      "8.0: I'm a quoted expression\n",
      "10.0: I'm a SimFunction\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"run! finished with 5 clock events, 0 sample steps, simulation time: 10.0\""
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "reset!(𝐶)                                ### reset the clock\n",
    "\n",
    "ev1 = :(println(tau(), \": I'm a quoted expression\"))\n",
    "ev2 = SF(() -> println(tau(), \": I'm a SimFunction\"))\n",
    "event!(ev1, at, 2)                       ### schedule an event at 2\n",
    "event!(ev1, after, 8)                    ### schedule an event after 8\n",
    "event!(ev2, every, 5)                    ### schedule an event every 5\n",
    "run!(𝐶, 10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "12.0: I'm a quoted expression\n",
      "12.0: I'm a SimFunction\n",
      "15.0: I'm a SimFunction\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"run! finished with 2 clock events, 0 sample steps, simulation time: 15.0\""
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "event!((ev1, ev2), after, 2)             ### schedule both ev1 and ev2 as event\n",
    "run!(𝐶, 5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Conditional events\n",
    "\n",
    "*Conditional events*  with (`event!`) execute under given conditions. Conditions can be formulated by using the `@tau` macro questioning the simulation time, the `@val` macro questioning a variable or any other logical expression or function or combinations of them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6.28999999999991: now y ≥ π\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"run! finished with 0 clock events, 1000 sample steps, simulation time: 10.0\""
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "reset!(𝐶)                                ### reset the clock\n",
    "y = 0                                    ### create a variable y\n",
    "sample!( SF(() -> global y = tau()/2) ); ### a sampling function\n",
    "event!( SF(()->println(tau(),\": now y ≥ π\") ), (@val :y :≥ π) ) ### a conditional event\n",
    "run!(𝐶, 10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6.283185307179586"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "2π"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6.809999999999899: now y ≥ 1/2\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"run! finished with 0 clock events, 1000 sample steps, simulation time: 10.0\""
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "reset!(𝐶)\n",
    "sample!( SF(()-> global y=sin(@tau)) );   ### sample a sine function on y\n",
    "event!(SF(()->println(tau(),\": now y ≥ 1/2\")), ((@val :y :≥ 1/2),(@tau :≥ 5))) ### two conditions\n",
    "run!(𝐶, 10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6.806784082777885"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "asin(0.5) + 2π                           ### exact value"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It can be seen: (1) the sample rate has some uncertainty in detecting events and (2) conditional events are triggered only once. If there is no sample rate set, a conditional event sets one up and deletes it again after it becomes true.\n",
    "\n",
    "## Processes\n",
    "\n",
    "Functions can be started as asynchronous [tasks or coroutines](https://docs.julialang.org/en/v1/manual/control-flow/#man-tasks-1), which can coordinate with the clock and events by delaying for some time or waiting for conditions, taking inputs from events or other tasks, triggering events or starting other tasks …\n",
    "\n",
    "From a modeling or simulation standpoint we call such tasks **processes**, because they can represent some ongoing activity in nature. Tasks seen as processes are a powerful modeling device, but you need to take care that\n",
    "\n",
    "1. they *give back control* to the clock and other such processes by calling delays or conditional waits or requesting resources (and thus implicitly waiting for them to become available) and\n",
    "2. they *get not out of sync* with simulation time by transferring critical operations to the clock.\n",
    "\n",
    "### Create and start a process\n",
    "\n",
    "`SimProcess`, alias `SP` prepares a function for running as a process and assignes it an id.  Then `process!` registers it to the clock and starts it as a process in a loop. You can define how many loops the function should persist, but the default is `Inf`. You can create as many instances of a function as processes as you like."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " 1.18: finished 1\n",
      " 2.72: finished 2\n",
      " 3.85: finished 3\n",
      " 4.77: finished 4\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"run! finished with 8 clock events, 0 sample steps, simulation time: 5.0\""
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "function doit(n)                           ### create a function doit\n",
    "    i = 1\n",
    "    while i ≤ n\n",
    "        delay!(rand()*2)                   ### delay for some time\n",
    "        now!(SF(println, @sprintf(\"%5.2f: finished %d\", tau(), i)))  ### print\n",
    "        i += 1\n",
    "    end\n",
    "end\n",
    "\n",
    "Random.seed!(1234);        \n",
    "reset!(𝐶)                                  ### reset the central clock\n",
    "process!(SP(1, doit, 5), 1)                ### create, register and start doit(5) as a process, id=1, runs only once\n",
    "run!(𝐶, 5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " 6.36: finished 5\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"run! finished with 2 clock events, 0 sample steps, simulation time: 7.0\""
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "run!(𝐶, 2)                                 ### it is not yet finished, run 2 more"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"run! finished with 0 clock events, 0 sample steps, simulation time: 10.0\""
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "run!(𝐶, 3)                                  ### doit(5) is done with 5, nothing happens anymore"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Delay, wait, take and put\n",
    "\n",
    "In order to synchronize with the clock, a process can\n",
    "- get the simulation time `tau(),\n",
    "- `delay!`, which suspends it until after the given time `t` or\n",
    "- `wait!` for a condition. This creates a conditional `event!` which reactivates the process when the conditions become true.\n",
    "\n",
    "Processes can also interact directly e.g. via [channels](https://docs.julialang.org/en/v1/manual/parallel-computing/#Channels-1) with [`take!`](https://docs.julialang.org/en/v1/base/parallel/#Base.take!-Tuple{Channel}) and [`put!`](https://docs.julialang.org/en/v1/base/parallel/#Base.put!-Tuple{Channel,Any}). This also may suspend them until there is something to take from a channel or until they are allowed to put something into it. In simulations they must take care that they keep synchronized with the clock."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " 6.24 Snoopy: yawn!, bark!, yawn!\n",
      " 6.50 Snoopy: wow wow wow wow wow wow wow wow \n",
      " 6.98 Snoopy: wow wow wow wow wow wow wow wow wow \n",
      " 7.37 Snoopy: smack smack smack\n",
      " 7.38 Snoopy: snore ... snore ... snore\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"run! finished with 10 clock events, 20 sample steps, simulation time: 10.0\""
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "function watchdog(name)\n",
    "    delay!(until, 6 + rand())            ### delay until\n",
    "    now!(SF(println, @sprintf(\"%5.2f %s: yawn!, bark!, yawn!\", tau(), name)))\n",
    "    wait!(((@val :hunger :≥ 7),(@tau :≥ 6.5)))   ### conditional wait\n",
    "    while 5 ≤ hunger ≤ 10\n",
    "        now!(SF(println, @sprintf(\"%5.2f %s: %s\", tau(), name, repeat(\"wow \", Int(trunc(hunger))))))\n",
    "        delay!(rand()/2)                 ### simple delay\n",
    "        if scuff\n",
    "            now!(SF(println, @sprintf(\"%5.2f %s: smack smack smack\", tau(), name)))\n",
    "            global hunger = 2\n",
    "            global scuff = false\n",
    "        end\n",
    "    end\n",
    "    delay!(rand())                       ### simple delay\n",
    "    now!(SF(println, @sprintf(\"%5.2f %s: snore ... snore ... snore\", tau(), name)))\n",
    "end\n",
    "\n",
    "hunger = 0\n",
    "scuff = false\n",
    "reset!(𝐶)\n",
    "Random.seed!(1122)\n",
    "\n",
    "sample!(SF(()-> global hunger += rand()), 0.5)   ### a sampling function: increasing hunger\n",
    "event!(SF(()-> global scuff = true ), 7+rand())  ### an event: scuff after 7 am\n",
    "process!(SP(1, watchdog, \"Snoopy\"), 1)           ### create, register and run Snoopy\n",
    "run!(𝐶, 10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Warning:\n",
    "You **must not** use or invoke operations like `delay!`, `wait!`, `take!` or `put!` outside of tasks and inside the Main process, because they will suspend it.\n",
    "\n",
    "### IO-operations\n",
    "\n",
    "If they invoke IO-operations like printing, reading or writing from or to files, tasks give control back to the Julia scheduler. In this case the clock may proceed further before the operation has been completed and the task has got out of sync with simulation time. Processes therefore should enclose IO-operations in a [`now!`](@ref) call. This will transfer them for execution to the clock, which must finish them before proceeding any further."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " 1.18: hi, here I am\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"run! finished with 1 clock events, 0 sample steps, simulation time: 10.0\""
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "function bad()                           ### bad: IO-operation DIY\n",
    "    delay!(rand()*2)\n",
    "    @printf(\"%5.2f: hi, here I am\\n\", tau())\n",
    "end\n",
    "Random.seed!(1234);\n",
    "reset!(𝐶)                                ### reset the clock\n",
    "process!(SP(1, bad), 5)                  ### setup a process with 5 cycles\n",
    "run!(𝐶, 10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " 1.18: hi, I am fine\n",
      " 2.72: hi, I am fine\n",
      " 3.85: hi, I am fine\n",
      " 4.77: hi, I am fine\n",
      " 6.36: hi, I am fine\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"run! finished with 10 clock events, 0 sample steps, simulation time: 10.0\""
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "function better()                        ### better: let the clock doit for you\n",
    "    delay!(rand()*2)\n",
    "    now!(SF(println, @sprintf(\"%5.2f: hi, I am fine\", tau())))\n",
    "end\n",
    "Random.seed!(1234);\n",
    "reset!(𝐶)                                ### reset the clock\n",
    "process!(SP(1, better), 5)               ### setup a process with 5 cycles\n",
    "run!(𝐶, 10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Continuous sampling\n",
    "\n",
    "Continuous sampling allows to bring continuous processes into a simulation or can be used for visualization or logging and collecting statistics.\n",
    "\n",
    "If you provide the clock with a time interval `Δt`, it ticks with a fixed sample rate. At each tick it will call registered functions or expressions:\n",
    "\n",
    "- `sample_time!(Δt)`: set the clock's sample rate starting from now.\n",
    "- `sample!(expr)`: register a function or expression for sampling. If no sample rate is set, set it implicitly.\n",
    "\n",
    "Sampling functions or expressions are called at clock ticks in the sequence they were registered. They are called before any events scheduled for the same time.\n",
    "\n",
    "**Note:** Conditions set by conditional `event!` or by `wait!` are also evaluated with the sampling rate. But the conditional event disappears after the conditions are met and the sample rate is then canceled if no sampling functions are registered.\n",
    "\n",
    "If no sample rate is set, the clock jumps from event to event.\n",
    "\n",
    "## Running a simulation\n",
    "\n",
    "After you have setup the clock, scheduled events, setup sampling or started processes – as you have seen – you can step or run through a simulation, stop or resume it.\n",
    "\n",
    "- `run!(sim::Clock, duration::Number)`: run a simulation for a given duration. Call all scheduled events and sampling actions in that timeframe.\n",
    "- `incr!(sim::Clock)`: take one simulation step, call the next tick or event.\n",
    "- `stop!(sim::Clock)`: stop a simulation\n",
    "- `resume!(sim::Clock)`: resume a halted simulation.\n",
    "\n",
    "\n",
    "## Logging\n",
    "\n",
    "Logging enables us to trace variables over simulation time and then analyze their behaviour.\n",
    "\n",
    "- `L = Logger()`: create a new logger, providing the newest record `L.last`, a logging table `L.df` and a switch `L.ltype` between logging types.\n",
    "- `init!(L::Logger, sim::Clock=𝐶)`:\n",
    "- `setup!(L::Logger, vars::Array{Symbol})`: setup `L`, providing it with an array of logging variables `[:a, :b, :c ...]`\n",
    "- `switch!(L::Logger, to::Number=0)`: switch between `0`: only keep the last record, `1`: print, `2`: write records to the table\n",
    "- `record!(L::Logger)`: record the logging variables with current simulation time.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "@webio": {
   "lastCommId": null,
   "lastKernelId": null
  },
  "kernelspec": {
   "display_name": "Julia 1.2.0",
   "language": "julia",
   "name": "julia-1.2"
  },
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "1.2.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
